#!/usr/bin/php
<?php

/**
 * pre-receive хук для GIT, запускается при выполнении команды push
 * производится проверка оформления кода с помощью PHPCodeSniffer
 */

error_reporting(E_ALL | E_STRICT);
$config = parse_ini_file("pre-receive.ini", false);
$extensions = explode(",", $config["extensions"]);

$outText = "";
ob_start();

	echo "--------------------------------------\n";
	echo "\n";
	echo "Start PHPCodeSniffer pre-receive hook\n";
	echo "\n";
	echo "--------------------------------------\n";
	
	// читаем входящие параметры
	// скрипт вызывается с параметрами sha1-old SP sha1-new SP refname LF
	$params = file_get_contents("php://stdin");	
	list($sha1Old, $sha1New, $refname) = preg_split("/[\s\t]/", $params);
	
	// создать временный каталог для этого push
	$tmpDir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $config["tempDir"] . DIRECTORY_SEPARATOR . $sha1New;
	if (!file_exists($tmpDir))
		mkdir($tmpDir, 0777, true);
	
	$changedFiles = null;
	// получим список только измененных файлов
	exec("git diff --name-only  {$sha1Old} {$sha1New}", $changedFiles);
	$changedFiles = implode(" ", $changedFiles);

	$out = null;
	// получаем инфу о дереве, которое содержит все изменения, присланные в push
	exec("git ls-tree -r {$sha1New} {$changedFiles}", $out);

	// общий результат проверки
	$fail = false;	
	
	foreach ($out as $item)
	{
		// Выходной формат команды git ls-tree: <mode> SP <type> SP <object> TAB <file>
		list($mode, $type, $object, $file) = preg_split("/[\s\t]/", $item);

		// инфа о файле
		$pathInfo = pathinfo($file);

		// не проверяем файлы в игнорируемых каталогах
		$dirName = $pathInfo["dirname"];

		$isSkipDir = false;
		foreach ($config["ignoreDir"] as $iDir)
		{
			if ($iDir == null)
				continue;
			if (strstr($dirName, trim($iDir, "/")) !== false)
			{
				echo "ignore directory {$dirName}\n";
				$isSkipDir = true;
				continue;
			}
		}

		if ($isSkipDir)
			continue;

		// проверяем только файлы с разрешенными расширениями
		if (!in_array(@$pathInfo["extension"], $extensions))
			continue;

		// не проверяем игнорируемые файлы
		if (in_array($file, $config["ignoreFile"]))
		{
			echo "ignore file {$file}\n";
			continue;
		}
			
		// создать структуру каталогов		
		$pathToSave = $tmpDir;		
		$baseName = $pathInfo["basename"];
		
		// если файл находится не в корне
		if ($dirName !== ".")
		{
			// То  пробуем создать каталог
			$pathToSave .= DIRECTORY_SEPARATOR . $dirName;
			if (!file_exists($pathToSave))
				mkdir($pathToSave, 0777, true);
		}		
		
		$res = null;
		// получить содержимое файла из push
		// и сохранить его во временный каталог
		$pathToSave .= DIRECTORY_SEPARATOR . $baseName;
		exec("git cat-file blob {$object} > {$pathToSave}", $res);
	
		// сохранить содержимое измененного файла во временный каталог
		echo "start checking {$file}\n";
		$res = null;
		$retVal = null;
		// выполняем команду проверки кода
		exec("{$config['phpcsCommand']} {$pathToSave}", $res, $retVal);
		
		if ($retVal !== 0)
			$fail = true;
		
		// вывод результата проверки в буфер
		foreach ($res as $message)
			echo "{$message}\n";

		echo "\n";	
	}

	// Удалить временный каталог и его содержимое
	recursiveCleanDir($tmpDir);

	if ($fail)
		echo "Result: push failed. Please, fix your code. (http://code.google.com/p/solo-framework/wiki/CodingStandard)\n";
	else
		echo "Result: ok\n";
	
	echo "\n--------------------------------------\n";
	echo "Author: Andrey Filippov <afi.work@gmail.com>\n";
	echo "--------------------------------------\n";

$outText = ob_get_contents();
ob_end_clean();
echo str_replace("\n", "\r\n", $outText);

// если при проверке кода обнаружились ошибки
// то завершим работу скрипта с кодом 1, чтобы GIT
// не принял присланные изменения
exit (intval($fail));


/**
 *
 * Рекурсивное удаление содержимого каталога
 * @param <type> $dir
 */
function recursiveCleanDir($dir)
{
	if (is_dir($dir))
	{
		$objects = scandir($dir);
		foreach ($objects as $object)
		{
			if ($object != "." && $object != "..")
			{
				if (filetype($dir . DIRECTORY_SEPARATOR . $object) == "dir")
					recursiveCleanDir($dir . DIRECTORY_SEPARATOR . $object); else
					unlink($dir . DIRECTORY_SEPARATOR . $object);
			}
		}
		reset($objects);
		rmdir($dir);
	}
}
?>